﻿/*
 * Socket.h
 *
 */

#ifndef __GDK_SOCKET_H__
#define __GDK_SOCKET_H__


#include <gdk/core/Types.h>
#include <gdk/core/String.h>
#include <gdk/core/Task.h>

#if !defined(_WIN32)
#	include <netinet/in.h>
#	include <netinet/tcp.h>
#	include <sys/socket.h>
#	include <sys/ioctl.h>
#	include <arpa/inet.h>
#	include <netdb.h>
#	include <errno.h>
#	include <unistd.h>
#else
#	include <winsock2.h>
#	include <ws2tcpip.h>
#endif



namespace gdk
{


#ifdef GDK_NET_DLL_BUILD
#	define GDK_NET_EXPORT	__DECLSPEC__(__DLLEXPORT__)
#elif !defined(GDK_NET_LIB_BUILD)
#	define GDK_NET_EXPORT	__DECLSPEC__(__DLLIMPORT__)
#else
#	define GDK_NET_EXPORT
#endif


#define GDK_NET_VERSION_MAJOR	2
#define GDK_NET_VERSION_MINOR	0
#define GDK_NET_VERSION_MICRO	0
#define GDK_NET_VERSION_INT		GDK_VERSION_INT(GDK_NET_VERSION_MAJOR,\
												GDK_NET_VERSION_MINOR,\
												GDK_NET_VERSION_MICRO)
#define GDK_NET_VERSION			GDK_VERSION(GDK_NET_VERSION_MAJOR,\
											GDK_NET_VERSION_MINOR,\
											GDK_NET_VERSION_MICRO)


/////////////////////////////////////////////////////
__INLINE__ bool operator <(const in_addr &a, const in_addr &b)
{
	return (*(uint32_t*)&a) < (*(uint32_t*)&b);
}
__INLINE__ bool operator ==(const in_addr &a, const in_addr &b)
{
	return (*(uint32_t*)&a) == (*(uint32_t*)&b);
}
__INLINE__ bool operator !=(const in_addr &a, const in_addr &b)
{
	return !(a == b);
}


__INLINE__ bool operator <(const in6_addr &a, const in6_addr &b)
{
	uint64_t *p1 = (uint64_t*)&a, *p2 = (uint64_t*)&b;
	if (p1[0] != p2[0])
		return (p1[0] < p2[0]);
	return (p1[1] < p2[1]);
}
__INLINE__ bool operator ==(const in6_addr &a, const in6_addr &b)
{
	uint64_t *p1 = (uint64_t*)&a, *p2 = (uint64_t*)&b;
	return (p1[0] == p2[0]) && (p1[1] == p2[1]);
}
__INLINE__ bool operator !=(const in6_addr &a, const in6_addr &b)
{
	return !(a == b);
}


#if defined(_WIN32)
#	pragma pack(push, 1)
#endif
	struct __in_addr {
		union {
			char			data[1];
			struct in_addr	ipv4;
			struct in6_addr	ipv6;
		};
		short				family;

	public:
		GDK_NET_EXPORT bool operator <(const __in_addr &addr) const;
		GDK_NET_EXPORT bool operator ==(const __in_addr &addr) const;
		__INLINE__ bool operator !=(const __in_addr &addr) const { return !(this->operator==(addr)); }
	}
#if defined(__GNUC__) || defined(__IOS__) || defined(__ANDROID__)
	__attribute__((packed))
#endif
	;
#if defined(_WIN32)
#	pragma pack(pop)
#endif


struct __sockaddr_in {
	union {
		struct sockaddr      ip;
		struct sockaddr_in   ipv4;
		struct sockaddr_in6  ipv6;
	};

public:
	GDK_NET_EXPORT String convertToString(void) const;
	GDK_NET_EXPORT bool operator ==(const __sockaddr_in &sa) const;
	__INLINE__ bool operator !=(const __sockaddr_in &sa) const { return !(this->operator==(sa)); }

	GDK_NET_EXPORT bool operator <(const __sockaddr_in &sa) const;
};


#if defined(__IPV6__)
#	define __AF_INET     AF_INET6
#else
#	define __AF_INET     AF_INET
#endif

#define SA_ZERO(sa)					(::memset(&sa, 0, sizeof(sa)))
#define SA_FAMILY(sa)				((sa).ip.sa_family)
#define SA_SET_FAMILY(sa, family)	((sa).ip.sa_family = uint16_t(family))

#define SA_SIZE(sa)					(SA_FAMILY(sa) == AF_INET6\
											? sizeof((sa).ipv6)\
											: sizeof((sa).ipv4))

#define SA_ADDR(sa)					(SA_FAMILY(sa) == AF_INET6\
											? ((char*)&(sa).ipv6.sin6_addr)\
											: ((char*)&(sa).ipv4.sin_addr))
#define SA_SET_ADDR(sa, addr)		(gdk::memCopy(SA_ADDR(sa), addr\
											, (SA_FAMILY(sa) == AF_INET6\
												? sizeof(struct in6_addr)\
												: sizeof(struct in_addr)\
											  )\
										)\
									)

#define SA_PORT(sa)					(SA_FAMILY(sa) == AF_INET6\
											? (ntohs((sa).ipv6.sin6_port))\
											: (ntohs((sa).ipv4.sin_port)))
#define SA_SET_PORT(sa, port)		do {\
										if (SA_FAMILY(sa) == AF_INET6)\
											(sa).ipv6.sin6_port = htons(port);\
										else\
											(sa).ipv4.sin_port = htons(port);\
									} while(0)


#if !defined(_WIN32)

#define SOCKET			int
#define INVALID_SOCKET	(-1)
#define SOCKET_ERROR	(-1)

#define WSAEWOULDBLOCK	EWOULDBLOCK
#define WSAEALREADY		EALREADY
#define WSAENOBUFS		ENOBUFS
#define WSAEHOSTUNREACH	EHOSTUNREACH
#define WSAECONNRESET	ECONNRESET
#define WSAECONNABORTED	ECONNABORTED

#endif


/////////////////////////////////////////////////////
#if defined(_DEBUG)
	extern GDK_NET_EXPORT bool NET_Debug;
	__INLINE__ void NET_SetDebug(bool flag) { NET_Debug = flag; }
#else
#	define NET_Debug false
	__INLINE__ void NET_SetDebug(bool) { }
#endif


/////////////////////////////////////////////////////
class GDK_NET_EXPORT Socket : public Task
{
	DECLARE_CLASS_HANDLE(Socket)

private:
	SOCKET	_socket;


protected:
	Socket(void);
	virtual ~Socket(void);

public:
	SOCKET getSocket(void) const;
	bool   isInvalid(void) const;
	bool   waitWriteable(int timeout = -1) const;
	bool   waitReadable(int timeout = -1) const;
	bool   setBufferSize(int sendBufSize, int recvBufSize);
	bool   getBufferSize(int &sendBufSize, int &recvBufSize) const;
	bool   setKeepAliveValue(int idle, int interval, int count);

	__INLINE__ bool isWriteable(void) const { return waitWriteable(0); }
	__INLINE__ bool isReadable(void) const { return waitReadable(0); }


public:
	virtual bool    attach(SOCKET s);
	virtual SOCKET  detach(void);
	virtual void    close(void);


public:
	static int	geterrno(void);
	static SOCKET	socket(
				int af,
				int type,
				int protocol
				);
	static SOCKET	accept(
				SOCKET           s,
				struct sockaddr  *addr,
				int              *addrlen
				);
	static int	bind(
				SOCKET                 s,
				const struct sockaddr  *name,
				int                    namelen
				);
	static int	connect(
				SOCKET                 s,
				const struct sockaddr  *name,
				int                    namelen
				);
	static int	getsockname(
				SOCKET           s,
				struct sockaddr  *name,
				int              *namelen
				);
	static int	getsockopt(
				SOCKET s,
				int    level,
				int    optname,
				char   *optval,
				int    *optlen
				);
	static int	ioctlsocket(
				SOCKET  s,
				long    cmd,
				unsigned long *argp
				);
	static int	listen(
				SOCKET s,
				int    backlog
				);
	static ssize_t recv(
				SOCKET  s,
				char   *buf,
				ssize_t len,
				int     flags
				);
	static int recvfrom(
				SOCKET           s,
				char            *buf,
				int              len,
				int              flags,
				struct sockaddr *from,
				int             *fromlen
				);
	static int	select(
				int                   nfds,
				fd_set               *readfds,
				fd_set               *writefds,
				fd_set               *exceptfds,
				const struct timeval *timeout
				);
	static ssize_t send(
				SOCKET       s,
				const char  *buf,
				ssize_t      len,
				int          flags
				);
	static int sendto(
				SOCKET                  s,
				const char             *buf,
				int                     len,
				int                     flags,
				const struct sockaddr  *to,
				int                     tolen
				);
	static int	setsockopt(
				SOCKET      s,
				int         level,
				int         optname,
				const char  *optval,
				int         optlen
				);
	static int	shutdown(
				SOCKET s,
				int    how
				);
	static int	closesocket(
				SOCKET s
				);
	static int	gethostname(
				char *name,
				int  namelen
				);
	static int	getpeername(
				SOCKET           s,
				struct sockaddr  *name,
				int              *namelen
				);
	static const struct hostent* gethostbyaddr(
				const char   *addr,
				int          len,
				int          type
				);
	static const struct hostent* gethostbyname(
				const char  *name
				);

	static int inet_addr(
				const char *cp
				);
	static const char* inet_ntoa(
				struct in_addr in
				);
	/*
	static uint32_t	htonl(
				uint32_t hostlong
				);
	static uint16_t	htons(
				uint16_t hostshort
				);
	static uint32_t	ntohl(
				uint32_t netlong
				);
	static uint16_t	ntohs(
				uint16_t netshort
				);
	*/

	static String getHostName(void);

	//========================================
	// Support IPv6
	static int getaddrinfo(
		const Char*    nodename,
		int*           family,
		int*           socktype,
		__sockaddr_in* sa);

	static const char* inet_ntop(
		int          family,
		const void*  src,
		char*        dst,
        int          cnt);

	static int inet_pton(
		int          family,
		const char*  src,
		void*        dst);
	//========================================
};





__INLINE__
Socket::~Socket(void)
{
	if(!isInvalid())
		Socket::close();
}


__INLINE__
SOCKET Socket::getSocket(void) const
{
	return _socket;
}

__INLINE__
bool Socket::isInvalid(void) const
{
	return	getSocket() == INVALID_SOCKET;
}

__INLINE__
SOCKET Socket::detach(void)
{
	SOCKET s = _socket;

	_socket = INVALID_SOCKET;

	return s;
}

__INLINE__
SOCKET	Socket::socket(
				int af,
				int type,
				int protocol
				)
{
	return ::socket(
			af,
			type,
			protocol
			);
}

__INLINE__
SOCKET	Socket::accept(
				SOCKET           s,
				struct sockaddr *addr,
				int             *addrlen
				)
{
#ifdef _WIN32

	return ::accept(
			s,
			addr,
			addrlen
			);

#else

	socklen_t len = *addrlen;
	SOCKET ret = ::accept(
			s,
			addr,
			&len
			);
	*addrlen = len;
	return ret;

#endif
}

__INLINE__
int	Socket::bind(
				SOCKET                 s,
				const struct sockaddr *name,
				int                    namelen
				)
{
	return ::bind(
			s,
			name,
			namelen
			);
}

__INLINE__
int	Socket::connect(
				SOCKET                 s,
				const struct sockaddr *name,
				int                    namelen
				)
{
	return ::connect(
				s,
				name,
				namelen
				);
}

__INLINE__
int	Socket::getsockname(
				SOCKET           s,
				struct sockaddr *name,
				int             *namelen
				)
{
#ifdef _WIN32

	return ::getsockname(
				s,
				name,
				namelen
				);

#else

	socklen_t len = *namelen;
	int ret = ::getsockname(
				s,
				name,
				&len
				);
	*namelen = len;
	return ret;

#endif
}

__INLINE__
int	Socket::getsockopt(
				SOCKET s,
				int    level,
				int    optname,
				char   *optval,
				int    *optlen
				)
{
#ifdef _WIN32

	return ::getsockopt(
				s,
				level,
				optname,
				optval,
				optlen
				);

#else

	socklen_t len = *optlen;
	int ret = ::getsockopt(
				s,
				level,
				optname,
				optval,
				&len
				);
	*optlen = len;
	return ret;

#endif
}

__INLINE__
int	Socket::ioctlsocket(
				SOCKET         s,
				long           cmd,
				unsigned long *argp
				)
{
#ifdef _WIN32

	return ::ioctlsocket(
				s,
				cmd,
				argp
				);

#else

	return ::ioctl(
				s,
				cmd,
				argp
				);

#endif
}

__INLINE__
int	Socket::listen(
				SOCKET s,
				int    backlog
				)
{
	return ::listen(
			s,
			backlog
			);
}

__INLINE__
ssize_t	Socket::recv(
				SOCKET  s,
				char   *buf,
				ssize_t len,
				int     flags
				)
{
	return ::recv(
			s,
			buf,
			static_cast<int>(min(len, ssize_t(INT_MAX))),
			flags
			);
}

__INLINE__
int	Socket::recvfrom(
				SOCKET           s,
				char            *buf,
				int              len,
				int              flags,
				struct sockaddr *from,
				int             *fromlen
				)
{
#ifdef _WIN32

	return ::recvfrom(
			s,
			buf,
			len,
			flags,
			from,
			fromlen
			);

#else

	socklen_t l = *fromlen;
	int ret = ::recvfrom(
			s,
			buf,
			len,
			flags,
			from,
			&l
			);
	*fromlen = l;
	return ret;

#endif

}

__INLINE__
int	Socket::select(
				int                   nfds,
				fd_set               *readfds,
				fd_set               *writefds,
				fd_set               *exceptfds,
				const struct timeval *timeout
				)
{
	return ::select(
			nfds,
			readfds,
			writefds,
			exceptfds,
			(struct timeval*)timeout //Linux 下这个参数没有 const
			);
}

__INLINE__
ssize_t Socket::send(
				SOCKET      s,
				const char *buf,
				ssize_t     len,
				int         flags
				)
{
	return ::send(
			s,
			buf,
			static_cast<int>(min(len, ssize_t(INT_MAX))),
			flags
			);
}

__INLINE__
int	Socket::sendto(
				SOCKET                 s,
				const char            *buf,
				int                    len,
				int                    flags,
				const struct sockaddr *to,
				int                    tolen
				)
{
	return ::sendto(
			s,
			buf,
			len,
			flags,
			to,
			tolen
			);
}

__INLINE__
int	Socket::setsockopt(
				SOCKET      s,
				int         level,
				int         optname,
				const char *optval,
				int         optlen
				)
{
	return ::setsockopt(
			s,
			level,
			optname,
			optval,
			optlen
			);
}

__INLINE__
int	Socket::shutdown(
				SOCKET s,
				int    how
				)
{
	return ::shutdown(
			s,
			how
			);
}

__INLINE__
int	Socket::closesocket(
				SOCKET s
				)
{

#ifdef _WIN32

	return ::closesocket(
				s
				);

#else

	return ::close(s);	

#endif

}

__INLINE__
int	Socket::gethostname(
				char *name,
				int   namelen
				)
{
	return ::gethostname(
				name,
				namelen
				);
}

__INLINE__
int	Socket::getpeername(
				SOCKET           s,
				struct sockaddr *name,
				int             *namelen
				)
{

#ifdef _WIN32

	return ::getpeername(
				s,
				name,
				namelen
				);

#else

	socklen_t len = *namelen;
	int ret = ::getpeername(
				s,
				name,
				&len
				);
	*namelen = len;
	return ret;

#endif

}

__INLINE__
const struct hostent* Socket::gethostbyaddr(
				const char *addr,
				int         len,
				int         type
				)
{
	return ::gethostbyaddr(
				addr,
				len,
				type
				);
}

__INLINE__
const struct hostent* Socket::gethostbyname(
				const char *name
				)
{
	return ::gethostbyname(
				name
				);
}

__INLINE__
int	Socket::geterrno(void)
{
#ifdef _WIN32
	return WSAGetLastError();
#else
	return errno;
#endif
}

/*
__INLINE__
uint32_t Socket::htonl(
				uint32_t hostlong
				)
{
	return ::htonl(
				hostlong
				);
}

__INLINE__
uint16_t Socket::htons(
				uint16_t hostshort
				)
{
	return ::htons(
				hostshort
				);
}

__INLINE__
uint32_t Socket::ntohl(
				uint32_t netlong
				)
{
	return ::ntohl(
				netlong
				);
}

__INLINE__
uint16_t Socket::ntohs(
				uint16_t netshort
				)
{
	return ::ntohs(
				netshort
				);
}
*/


__INLINE__
int Socket::inet_addr(
				const char *cp
				)
{
	return ::inet_addr(
				cp
				);
}

__INLINE__
const char* Socket::inet_ntoa(
				struct in_addr in
				)
{
	return ::inet_ntoa(
				in
				);
}






}//namespace gdk




#endif /*__GDK_SOCKET_H__*/
